/*
 * symboltable.c
 *
 *  Created on: Jan 14, 2014
 *      Author: Benjamin Groeber, Julian Sanin, Matthaeus Ranigler
 */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "symboltable.h"

static symbol_t *symbolNames = NULL;

static symbol_t *findEncodedSymbol(const char *encodedSymbolName);
static char *encodeScopeLevel(const char *symbolName, int scopeLevel);
static void printSymbol(const symbol_t *s);
static void removeSymbol(symbol_t *symbol);

void addSymbolInt32(char *symbolName, int scopeLevel, int value) {
	char *encodedSymbolName = encodeScopeLevel(symbolName, scopeLevel);
	symbol_t *s = findEncodedSymbol(encodedSymbolName);
	if(s == NULL) {
		s = (symbol_t *)malloc(sizeof(symbol_t));
		s->name = encodedSymbolName;
		HASH_ADD_KEYPTR(hh, symbolNames, s->name, strlen(s->name), s);
	}
	s->type = INT32;
	s->value.int32 = value;
	free(symbolName);
}

void addSymbolFP32(char *symbolName, int scopeLevel, float value) {
	char *encodedSymbolName = encodeScopeLevel(symbolName, scopeLevel);
	symbol_t *s = findEncodedSymbol(encodedSymbolName);
	if(s == NULL) {
		s = (symbol_t *)malloc(sizeof(symbol_t));
		s->name = encodedSymbolName;
		HASH_ADD_KEYPTR(hh, symbolNames, s->name, strlen(s->name), s);
	}
	s->type = FP32;
	s->value.fp32 = value;
	free(symbolName);
}

char *encodeScopeLevel(const char *symbolName, int scopeLevel) {
	char scopeLevelStr[12] = { '\0' };
	snprintf(scopeLevelStr, sizeof(scopeLevelStr), "%d", scopeLevel);
	char *encodedSymbolName = (char *)malloc(
			strlen(scopeLevelStr) + strlen(symbolName) + 1);
	strcpy(encodedSymbolName, scopeLevelStr);
	strcat(encodedSymbolName, symbolName);
	return encodedSymbolName;
}

symbol_t *findEncodedSymbol(const char *encodedSymbolName) {
	symbol_t *s;
	HASH_FIND_STR(symbolNames, encodedSymbolName, s);
	return s;
}

symbol_t *findSymbol(char *symbolName, int scopeLevel) {
	symbol_t *s = NULL;
	int level = scopeLevel;
	while(level >= 0) {
		char *encodedSymbolName = encodeScopeLevel(symbolName, level);
		HASH_FIND_STR(symbolNames, encodedSymbolName, s);
		free(encodedSymbolName);
		if(s != NULL) {
			break;
		}
		level--; // Go up to parent scope.
	}
	//free(symbolName);
	return s;
}

int decodeScopeLevel(const char *symbolName) {
	int scopeLevel = atoi(symbolName);
	return scopeLevel;
}

void removeAllSymbolsFromScopeLevel(int scopeLevel) {
	symbol_t *current, *temp;
	HASH_ITER(hh, symbolNames, current, temp) {
		if(decodeScopeLevel(current->name) == scopeLevel) {
			removeSymbol(current);
		}
	}
}

void removeAllSymbols() {
	symbol_t *current, *temp;
	HASH_ITER(hh, symbolNames, current, temp) {
		removeSymbol(current);
	}
}

void removeSymbol(symbol_t *symbol) {
	HASH_DEL(symbolNames, symbol);
	free(symbol->name);
	free(symbol);
}

void printSymbol(const symbol_t *s) {
	switch(s->type) {
	case INT32:
		printf("[%s][%d]\n", s->name, s->value.int32);
		break;
	case FP32:
		printf("[%s][%.4f]\n", s->name, s->value.fp32);
		break;
	default:
		printf("[%s][UNKNOWN]\n", s->name);
	}
}

void printAllSymbols() {
	symbol_t *s;
	for(s = symbolNames; s != NULL; s = (symbol_t *)(s->hh.next)) {
		printSymbol(s);
	}
}
